#include "keypad.h"
#include "utility.h"
#include "common.h"


CKeyPad::CKeyPad()
:m_keyCurrent(0),
m_keyPressed(0),
m_keyReleased(0),
m_keysDisabled(0),
m_keyTick(0),
m_lastPressed(0),
m_keyDblPressed(0),
m_fastCurrentKey(0),
m_queueStart(0),
m_queueEnd(0),
m_tickCounter(0)
{
	memset(m_lastPress, 0, sizeof(long) * GAME_KEY_COUNT);
	memset(m_intervalPress, 0, sizeof(long) * GAME_KEY_COUNT);
	memset(m_gkQueue, 0, sizeof(int) * GAMEKEY_QUEUE_SIZE);
	memset(m_gkTimeQueue, 0, sizeof(long) * GAMEKEY_QUEUE_SIZE);
}

CKeyPad::~CKeyPad()
{

}

void CKeyPad::InitKey()
{
	m_keyCurrent = 0;
	m_keyPressed = 0;
	m_keyReleased = 0;
	m_keyDblPressed = 0;
	m_lastPressed = 0;
	m_keyTick = 0;
	m_fastCurrentKey = 0;
	for(int i = 0; i < GAMEKEY_QUEUE_SIZE; i ++)
	{
		m_gkQueue[i] = 0;
		m_gkTimeQueue[i] = 0;
	}
	m_queueStart = m_queueEnd = 0;
}

void CKeyPad::ClearKey()
{
	m_keyCurrent = 0;
	m_keyPressed = 0;
	m_keyReleased = 0;
	m_keyDblPressed = 0;
	m_lastPressed = 0;
	m_keyTick = 0;
	m_fastCurrentKey = 0;
}

void CKeyPad::DisableKey(int frames)
{
	m_keysDisabled = frames;
}

bool CKeyPad::PushQueue(unsigned int key)
{
	if( (m_queueStart + 1) % GAMEKEY_QUEUE_SIZE == m_queueEnd)
	{
		return false;
	}
	else
	{
		m_gkQueue[m_queueStart] = key;
		m_queueStart = (unsigned int)(( m_queueStart + 1 ) % GAMEKEY_QUEUE_SIZE);
	}
	return true;
}

int CKeyPad::PopQueue()
{
	if(m_queueStart == m_queueEnd)
	{
		return m_fastCurrentKey;
	}
	int key = m_gkQueue[m_queueEnd];
	m_queueEnd = (unsigned int) ( (m_queueEnd + 1 ) % GAMEKEY_QUEUE_SIZE );
	return key;
}

void CKeyPad::UpdateKey()
{
	if(m_keysDisabled > 0)
	{
		m_keysDisabled--;

		m_keyCurrent = 0;
		m_keyPressed = 0;
		m_keyReleased = 0;
		m_keyDblPressed = 0;
		m_lastPressed = 0;
		m_keyTick = 0;
		m_fastCurrentKey = 0;

		return;
	}

	unsigned int currentKey;
	currentKey = PopQueue();
	m_keyPressed = ~m_keyCurrent & currentKey;
	m_keyReleased = m_keyCurrent & ~currentKey;
	m_keyCurrent = currentKey;
	m_keyTick ++;
	if (m_keyPressed != 0)
	{
		//for double key only
		if ((m_lastPressed != 0)
			&& m_keyTick < DOUBLEPRESS_INTERVAL
			&& (m_lastPressed == m_keyPressed))
		{
			m_keyDblPressed = m_keyPressed;
			m_lastPressed = 0;
		}
		else
		{
			m_keyTick = 0;
			m_keyDblPressed = 0;
			m_lastPressed = m_keyPressed;
		}
	}
	else if (m_keyDblPressed != 0)
	{
		m_keyDblPressed = 0;
	}
}

bool CKeyPad::IsKeyHold(unsigned int gameKey)
{
	return (m_keyCurrent & gameKey) != 0;
}

bool CKeyPad::IsAnyKeyPressed()
{
	return m_keyPressed != 0;
}

bool CKeyPad::IsKeyDoublePressed(unsigned int gameKey)
{
	return (m_keyDblPressed & gameKey) != 0;
}

bool CKeyPad::IsKeyPressed(unsigned int keyCode)
{
	return (m_keyPressed & keyCode) != 0;
}

bool CKeyPad::IsKeyReleased(unsigned int keyCode)
{
	return (m_keyReleased & keyCode) != 0;
}

void CKeyPad::KeyPressed(unsigned int keyCode)
{
	m_fastCurrentKey |= GetKey(keyCode);
	if (m_keyCurrent == 0)
	{
		PushQueue(m_fastCurrentKey);
		int index = Utility::log2(m_fastCurrentKey);
		m_lastPress[index] = m_tickCounter;
	}
	else 
	{
		return;
	}  
}

void CKeyPad::KeyReleased(unsigned int keyCode)
{
	m_fastCurrentKey &= ~GetKey(keyCode);
	PushQueue(m_fastCurrentKey);
}

bool CKeyPad::IsAnyKeyHold()
{
	return m_keyCurrent != 0;
}

long CKeyPad::GetKeyInterval(unsigned int gameKey)
{
	return m_tickCounter - m_lastPress[Utility::log2(gameKey)];
}

int CKeyPad::GetKey(unsigned int keyCode)
{
	switch (keyCode)
	{
	case NOK_UP_ARROW:
		return GK_PAD_UP;            	
	case KEY_NUM2:
		return GK_UP;
	case NOK_DOWN_ARROW:
		return GK_PAD_DOWN;               	
	case KEY_NUM8:
		return GK_DOWN;
	case NOK_LEFT_ARROW:
		return GK_PAD_LEFT;            	
	case KEY_NUM4:            
		return GK_NUM4;            
	case NOK_RIGHT_ARROW:
		return GK_PAD_RIGHT;                
	case KEY_NUM6:
		return GK_NUM6;
	case KEY_NUM1:
		return GK_NUM1;
	case KEY_NUM7:
		return GK_NUM7;
	case KEY_NUM3:
		return GK_UPRIGHT;
	case KEY_NUM9:
		return GK_NUM9;
	case NOK_MIDDLE:
		return GK_NUM5;
	case KEY_NUM5:
		return GK_NUM5;
	case KEY_POUND:
		return GK_POUND;
	case KEY_NUM0:
		return GK_NUM0;
	case KEY_STAR:
		return GK_STAR;
	case NOK_LEFT_SOFT:
		return GK_LEFT_SOFT;
	case NOK_RIGHT_SOFT:
		return GK_RIGHT_SOFT;             
	default:
		return 0;
	}
}